Add safe context watcher API - #6227
Conversation
eb0dff3 to
66ad857
Compare
7259b06 to
69b141c
Compare
|
LGTM, but im not a maintainer |
|
Still, thank you for taking a look. |
b253b69 to
2041732
Compare
## Description Add a generic CPython 3.14+ publisher for the internal `python.context.switch` event. It registers the CPython `PyContext_AddWatcher` API from the native extension and dispatches the event after each context switch. The C callback catches Rust panics at the FFI boundary and preserves any exception already pending when CPython invokes it. This PR is stacked on #19604, which adds the public `DD_TRACE_OTEL_CTX_ENABLED` configuration and activation-listener setup. The setting defaults to `true`; setting it to `false` disables both the listener and this Python 3.14 watcher. It also complements #19336, which independently provides the asyncio, uvloop, AnyIO, and greenlet emitters for older Python versions. ## Testing - `scripts/lint checks` - `reno lint` - CPython 3.14 native context watcher: 4 passed - Linux CPython 3.14.5 OTel thread context: 6 passed ## Risks Low ## Additional Notes - The PR contains raw external C bindings to CPython. I am currently upstreaming them to PyO3; once that work is merged and released, we can rely on the PyO3 bindings and simplify this code: - PyO3/pyo3#6204 - PyO3/pyo3#6227 Co-authored-by: florentin.labelle <florentin.labelle@datadoghq.com>
d0a8de6 to
cf95e72
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
|
I would quite like to avoid the macro for this API, it seems unfortunate. I understand why it's there, however. I'm thinking about what we can do as alternatives. |
davidhewitt
left a comment
There was a problem hiding this comment.
I opened florentinl#1 which has a suggestion how I'd like this API to look.
Also placed a few other comments here.
cf95e72 to
e4ac1c4
Compare
|
Thank you a lot @davidhewitt for the thorough review and your design suggestion. I updated the PR to remove the macro API in favor of the design you suggested and tried to address your other comments. |
davidhewitt
left a comment
There was a problem hiding this comment.
Thanks, this generally looks ready to me, just some small nits.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a4afcdbe6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
a9e5201 to
c0f278b
Compare
Fix the newsfragment's overclaim that PyContext requires GIL-enabled CPython 3.14+ (it's available on all supported versions; only the watcher API needs 3.14+), clarify the SAFETY comments on ContextWatcherGuard around single-interpreter attachment, and drop a redundant doctest cfg attribute already covered by the module-level gate in lib.rs.
Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
c0f278b to
9def052
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9def05278d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // attaching to more than one interpreter, so this is the interpreter for which the watcher | ||
| // was registered | ||
| // - `watcher_id` was returned by `PyContext_AddWatcher` | ||
| error_on_minusone(py, unsafe { ffi::PyContext_ClearWatcher(watcher_id) }) |
There was a problem hiding this comment.
Defer clearing watchers during callback dispatch
On GIL-enabled CPython 3.14.4, if an earlier watcher uses safe shared state to call clear on a later watcher, this immediately nulls the later callback slot while CPython is still iterating a snapshot of the active-watcher bitmask; CPython subsequently calls that null slot and segfaults. The drop path has the same issue because it also calls PyContext_ClearWatcher immediately, so the safe API permits memory-unsafe behavior. Track callback dispatch and defer removals until it completes (or otherwise prevent clearing another watcher from within a callback).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I don't really know how it should be handled in PyO3.
python/cpython#155943 which was opened to fix python/cpython#155619 solves the problem.
There was a problem hiding this comment.
For now I think we can call this a CPython bug and merge this as-is in PyO3; it's a silly edge case which I think we can allow to crash even if it's not safe.
|
Looks like something still not properly skipped in the wasm tests. |
Adds
PyContextand safe context-watcher bindings for GIL-enabled CPython 3.14 and newer:PyContextitself is available on all supported CPython versions.Tested with unit tests, doctests, and Clippy on Python 3.14.